package com.dcy.machine.service;

import com.dcy.machine.model.MachineEvent;
import com.dcy.machine.model.MachineState;
import com.dcy.machine.model.MatchineObj;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.StateMachineContext;
import org.springframework.statemachine.StateMachinePersist;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.persist.DefaultStateMachinePersister;
import org.springframework.statemachine.persist.StateMachinePersister;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;

/**
 * @Author：dcy
 * @Description:
 * @Date: 2021/11/9 15:55
 */
@Slf4j
@Service
public class BusinessStateMachineService implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    /**
     * 状态机下一步
     *
     * @param matchineObj 状态对象
     * @param event       事件对象
     * @param <S>
     * @param <E>
     */
    @Deprecated
    public <S extends Enum<S> & MachineState, E extends Enum<E> & MachineEvent> boolean next(final MatchineObj<S> matchineObj, final E event) {
        AtomicBoolean success = new AtomicBoolean(Boolean.FALSE);
        final StateMachinePersister<S, E, MatchineObj<S>> stateMachinePersist = getStateMachinePersist(matchineObj, event);
        final StateMachineFactory<S, E> stateMachineFactory = getStateMachineFactory(matchineObj);
        final StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine(matchineObj.matchineId());
        try {
            // 恢复状态
            stateMachinePersist.restore(stateMachine, matchineObj);
            final Message<E> data = MessageBuilder.withPayload(event).build();
            // 执行下一步
            stateMachine.sendEvent(Mono.just(data))
                    .subscribe(machineEventResult -> {
                        switch (machineEventResult.getResultType()) {
                            case ACCEPTED:
                                success.set(Boolean.TRUE);
                                break;
                            case DEFERRED:
                            case DENIED:
                                // 拒绝的
                                throw new IllegalArgumentException("改状态失败: machineId=" + matchineObj.matchineId() +
                                        ", factoryName=" + matchineObj.factoryName() +
                                        ", state=" + matchineObj.stateName() +
                                        ", event=" + event.name());
                            default:
                        }
                    });
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("改状态失败: machineId=" + matchineObj.matchineId() +
                    ", factoryName=" + matchineObj.factoryName() +
                    ", state=" + matchineObj.stateName() +
                    ", event=" + event.name());
        } finally {
            stateMachine.stopReactively().subscribe();
        }
        return success.get();
    }

    /**
     * 状态机下一步
     * 使用注意：先执行状态流转，然后在操作业务。具体用乐观锁或者分布式锁都可以
     *
     * @param matchineObj    状态对象
     * @param event          事件对象
     * @param resultConsumer 成功之后消费
     * @param <S>
     * @param <E>
     */
    public <S extends Enum<S> & MachineState, E extends Enum<E> & MachineEvent> void next(final MatchineObj<S> matchineObj, final E event, Consumer<MatchineObj<S>> resultConsumer) {
        final StateMachinePersister<S, E, MatchineObj<S>> stateMachinePersist = getStateMachinePersist(matchineObj, event);
        final StateMachineFactory<S, E> stateMachineFactory = getStateMachineFactory(matchineObj);
        final StateMachine<S, E> stateMachine = stateMachineFactory.getStateMachine(matchineObj.matchineId());
        try {
            // 恢复状态
            stateMachinePersist.restore(stateMachine, matchineObj);
            final Message<E> data = MessageBuilder.withPayload(event).build();
            // 执行下一步
            stateMachine.sendEvent(Mono.just(data))
                    .subscribe(machineEventResult -> {
                        switch (machineEventResult.getResultType()) {
                            case ACCEPTED:
                                // 认可的
                                Optional.ofNullable(resultConsumer).ifPresent(matchineObjConsumer -> {
                                    matchineObjConsumer.accept(matchineObj);
                                });
                                break;
                            case DEFERRED:
                                // 推迟的
                            case DENIED:
                                throw new IllegalArgumentException("改状态失败: machineId=" + matchineObj.matchineId() +
                                        ", factoryName=" + matchineObj.factoryName() +
                                        ", state=" + matchineObj.stateName() +
                                        ", event=" + event.name());
                            default:
                        }
                    });
        } catch (Exception e) {
            e.printStackTrace();
            throw new IllegalArgumentException("改状态失败: machineId=" + matchineObj.matchineId() +
                    ", factoryName=" + matchineObj.factoryName() +
                    ", state=" + matchineObj.stateName() +
                    ", event=" + event.name());
        } finally {
            stateMachine.stopReactively().subscribe();
        }
    }


    /**
     * 获取持久化对象（控制流程能不能走下一步）
     *
     * @param matchineObj 状态对象
     * @param event       事件对象
     * @param <S>
     * @param <E>
     * @return
     */
    private <S extends Enum<S> & MachineState, E extends Enum<E> & MachineEvent> DefaultStateMachinePersister<S, E, MatchineObj<S>> getStateMachinePersist(MatchineObj<S> matchineObj, E event) {
        final StateMachinePersist<S, E, MatchineObj<S>> stateMachinePersist = new StateMachinePersist<S, E, MatchineObj<S>>() {

            @Override
            public void write(StateMachineContext<S, E> context, MatchineObj<S> contextObj) {
                //这里不做任何持久化工作
            }

            @Override
            public StateMachineContext<S, E> read(MatchineObj<S> contextObj) {
                final Class<S> aClass = contextObj.machineStateEnum();
                return new DefaultStateMachineContext<S, E>(S.valueOf(aClass, contextObj.stateName()),
                        null,
                        null,
                        null,
                        null,
                        matchineObj.matchineId());
            }
        };
        return new DefaultStateMachinePersister<>(stateMachinePersist);
    }

    /**
     * 根据状态对象 获取状态机工厂
     *
     * @param matchineObj 状态对象
     * @param <S>
     * @param <E>
     * @return
     */
    private <S extends Enum<S> & MachineState, E extends Enum<E> & MachineEvent> StateMachineFactory<S, E> getStateMachineFactory(MatchineObj<S> matchineObj) {
        return applicationContext.getBean(matchineObj.factoryName(), StateMachineFactory.class);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}
